//  基于fetch封装的请求方法
import { createFetch } from '@vueuse/core'
import { getMimeType } from '@/utils/util'

type HttpMethod = 'get' | 'post' | 'put' | 'delete'
type SendType = 'body' | 'params'
type ResponseType = 'json' | 'text' | 'blob'
interface RequestOptions {
  /** 请求地址 */
  url: string
  /** 请求方法,默认get */
  method?: HttpMethod
  /** 请求参数 */
  data?: any
  /** 是否需要token，默认需要 */
  useToken?: boolean
  /** 超时时间,默认200毫秒 */
  timeout?: number
  /** 传参方式,默认会根据method自动判断 即post/put/会传body,get/delete会传params,但难免有时候会有奇怪的情况,所以需要手动指定 */
  sendType?: SendType
  /** 返回数据格式 */
  responseType?: ResponseType
  /** fetch的其他选项 */
  customFetchOptions?: RequestInit
}

interface ResponseData<T> {
  code: number
  data: T
  msg: string
}

const request = async <T>(requestOptions: RequestOptions) => {
  let { url, sendType } = requestOptions
  const {
    method = 'get',
    data,
    customFetchOptions,
    responseType = 'json',
    timeout = 200,
    useToken = true,
  } = requestOptions

  // 如果没有传sendType，根据method进行简单的判断
  if (!sendType) {
    if (['get', 'delete'].includes(method)) {
      sendType = 'params'
    }
    if (['post', 'put'].includes(method)) {
      sendType = 'body'
    }
  }

  // 根据sendType处理请求参数
  let body: any = null
  if (sendType === 'body') {
    body = JSON.stringify(data)
  } else {
    url += '?' + new URLSearchParams(data).toString()
  }

  const fetchRequestOptions: RequestInit = {
    credentials: 'include',
    cache: 'no-cache',
    headers: {
      'Content-Type': 'application/json',
    },
    method,
    body,
    ...customFetchOptions,
  }

  const useDefaultFetch = createFetch({
    baseUrl: '/api',
    combination: 'overwrite',
    options: {
      timeout,
      beforeFetch({ options }) {
        // 处理token
        if (useToken) {
          const token = localStorage.getItem('token') || ''
          if (token) {
            // 注入token
            //  @ts-ignore
            options!.headers!.Authorization = `Bearer ${token}`
          }
        }
        return { options }
      },
    },
    fetchOptions: fetchRequestOptions,
  })

  const res = useDefaultFetch(url)

  const { error } = res
  if (error.value) {
    // $message.error('请求错误')
    console.error('请求错误')
    return Promise.reject('请求错误')
  }

  if (responseType === 'json') {
    const { data: resData } = await res.json()
    const { data: responseData, msg, code } = resData.value as ResponseData<T>
    if (code !== 200) {
      //   $message.error(msg)
      console.error(msg)
      return Promise.reject(msg)
    }
    return Promise.resolve(responseData)
  } else if (responseType === 'text') {
    const { data: resData } = await res.text()
    return Promise.resolve(resData as T)
  } else if (responseType === 'blob') {
    const { data: resData, response } = await res.blob()

    const fileName = response!.value!.headers.get('Content-Disposition')?.split('filename=')[1]

    //  从fileName 获取文件后缀
    const suffix = fileName!.split('.')[1]
    const mimeType = getMimeType(suffix)
    const blob = new Blob([resData.value as Blob], { type: mimeType })
    const blobUrl = globalThis.URL.createObjectURL(blob)
    return Promise.resolve({
      blobUrl,
      fileName,
    } as T)
  }
}

export default request
